home *** CD-ROM | disk | FTP | other *** search
- //
- // Heightfield Engine
- // written by: Alex Chalfin
- // email: achalfin@uceng.uc.edu
- //
- // Tested with Watcom C/C++ v10.0 and v10.5
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <conio.h>
- #include <string.h>
- #include <malloc.h>
- #include <math.h>
- #include <time.h>
- #include <i86.h>
-
- // the MAPSHIFT constant controls the size of the map. The map dimentions
- // are (2^MAPSHIFT)x(2^MAPSHIFT). So if you set MAPSHIFT equal to 8, you
- // will get a 256x256 map.
- #define MAPSHIFT 8 // 2^MAPSHIFT = size of map
-
- // Macros dependant on MAPSHIFT. They change with the size of the map. They
- // should not be changed manually.
- #define MAPSIZE (1<<MAPSHIFT) // size of one side of the map
- #define BYTE(a) (a & (MAPSIZE-1)) // put the range within MAPSIZE
- #define INDEX(x,y) ((BYTE(y) << MAPSHIFT) + BYTE(x)) // index calculation
- #define fINDEX(x,y) (INDEX(x >> 8, y >> 8)) // fixed point index calculation
-
- // A couple of miscelanous constants
- #define NUMSMOOTH 3 // smooth the map 3 times
- #define FARDEPTH 40 // number of depth steps
- #define NEARDEPTH 0 // do not change this number....
- #define NUMSTEPS FARDEPTH-NEARDEPTH
- #define HEIGHTMUL 16 // perspective multiplication factor
- #define SLOPE 2 // do not change this number....
- #define RENDERWIDTH 320 // vertical scanlines to render
-
- #define RANDOM(a) (rand() / (RAND_MAX / (a))) // a simple random function
-
- typedef struct tview // a "view" on the voxel map
- { // uses 3 points to define a view
- int x1, y1, x2, y2; // a center or viewer position and a point to the
- int cx, cy; // left and a point to the right. The side points are
- int z;
- } tview; // used to define the viewer rotation and where to
- // trace on the height map
-
- typedef struct hrec // struct to store a height and a color for a vertical
- { // line for the rendering process.
- int y;
- int c;
- } hrec;
-
- typedef struct // 4 byte division for union use
- {
- char b1, b2, b3, b4;
- } _bytes;
-
- typedef union register32_8 // simulate a byte accessable 32-bit register
- {
- int dword;
- _bytes bytes;
- } register32_8;
-
- char *heightmap; // height feild for the voxels
- char *colormap; // colors for the voxels
- char *vpage; // a virtual screen page
- hrec *buf1; // buffer for storing the "top" part
- hrec *buf2; // buffer for storing the "bottom" part
- int divtable[256]; // a 256/x table
- int sine[256]; // sin lookup table
- int cosine[256]; // cos lookup table
-
- void createmaps();
- void createfractalmap(int x1, int y1, int x2, int y2);
- char newcolor(int mc, int n, int dvd);
- void cleanup();
- void smoothmap();
- void makecolormap();
- void buildtables();
- void drawscreen(tview *v);
- void gouraudbuffers();
- void makepalette();
- void setrgb(char n, char r, char g, char b);
- void setview(int speed, int viewangle, int moveangle, int viewer, tview *v);
- short int initmouse();
- void readmickey(short int *x, short int *y);
-
-
- void vmode(short int);
- // video mode initialization routine. Use to init mode 13h and mode 03h
- #pragma aux vmode = \
- "int 10h" \
- parm [ax] \
- modify [ax];
-
- void vpagecopy(void *src);
- // virtual page copy and clear
- #pragma aux vpagecopy = \
- "Mov edx,esi" \
- "Mov edi,0a0000h" \
- "Mov ecx,16000" \
- "Mov eax,ecx" \
- "Rep Movsd" \
- "Mov ecx,eax" \
- "Xor eax,eax" \
- "Mov edi,edx" \
- "Rep Stosd" \
- parm [esi] \
- modify [ecx edx edi eax];
-
- main()
- {
- tview mainview; // current view
- int st, et; // time counters for FPS calculations
- int frame; // frame counter for FPS calculations
- int actionflag; // mouse available flag
- short int x; // mouse mickey "x" counter
- short int y; // mouse mickey "y" counter
-
-
- createmaps(); // build the terrain and color maps
- buildtables(); // build sin/cos and div tables
- vmode(0x13); // set the video mode
- makepalette(); // set nice read fade palette
- frame = 0;
- st = clock(); // starting time counter
-
- // initialize view
- setview(0, 0, 0, heightmap[fINDEX(mainview.cx, mainview.cy)], &mainview);
-
- actionflag = initmouse(); // set flag if a mouse is available
-
- while (inp(0x60) != 1)
- {
- if (actionflag == 0)
- // automate movement because mouse isn't availabe
- setview(256, 0, 0, heightmap[fINDEX(mainview.cx, mainview.cy)], &mainview);
- else
- {
- // set view based on mouse movement
- readmicky(&x, &y);
- mainview.cx += -y*4;
- mainview.x1 += -y*4;
- mainview.x2 += -y*4;
- mainview.cy += x*4;
- mainview.y1 += x*4;
- mainview.y2 += x*4;
- mainview.z = heightmap[fINDEX(mainview.cx, mainview.cy)];
- }
- drawscreen(&mainview); // render the image to the virtual screen
- vpagecopy(vpage); // copy the virtual screen to video memory
- frame++;
- }
- et = clock();
- vmode(0x03);
- printf("%5.2f frames per second\n\n", ((float)(frame*CLOCKS_PER_SEC))/(et-st));
- cleanup();
- printf("Voxel code by Alex Chalfin\n");
- printf("email: achalfin@uceng.uc.edu\n");
- }
-
- void setview(int speed, int viewangle, int moveangle, int viewer, tview *v)
- // sets the viewpoint parameters
- // in: speed - the speed you are traveling in 24.8 fixed point
- // viewangle - the angle to look. range 0..255
- // moveangle - the angle to move. range 0..255
- // viewer - height of the viewer
- // v - tview to set parameters of
- {
- // update the center position based on speed and move angle
- moveangle &= 255;
- v->cx += ((speed*cosine[moveangle]) >> 8);
- v->cy += ((speed*sine[moveangle]) >> 8);
-
- // set viewing angle
- viewangle &= 255;
- v->x1 = (FARDEPTH * cosine[viewangle]) + (SLOPE*FARDEPTH * sine[viewangle]) + v->cx;
- v->y1 = (FARDEPTH * sine[viewangle]) - (SLOPE*FARDEPTH * cosine[viewangle]) + v->cy;
-
- v->x2 = (FARDEPTH * cosine[viewangle]) - (SLOPE*FARDEPTH * sine[viewangle]) + v->cx;
- v->y2 = (FARDEPTH * sine[viewangle]) + (SLOPE*FARDEPTH * cosine[viewangle]) + v->cy;
-
- v->z = viewer;
- }
-
- void drawscreen(tview *v)
- // This is the main drawing routine.
- // It takes a TVIEW structure as a parameter and renders the scene for you.
- // It will render at any rotation (depends on the TVIEW passed in)
- // it does not do view tilting (easily added).
- // Based on 24.8 fixed point and does proper map reapeating when necessary
- {
- int x,y,i; // general counters
- int xstep, xval, ystep, yval; // constant z interpolation vars
- int viewer, per; // perspective vars
- int lxval1, lxval2, lyval1, lyval2; // map tracing vars
- int lxstep1, lxstep2, lystep1, lystep2;
- static int leest[RENDERWIDTH]; // interpolation index list
- int index, oldi; // map location and old map location
- hrec *tbuf; // buf record. for pointer walks
-
- viewer = v->z + 20; // set appropriate viewing position
-
- // set up map traversing vars
- lxval1 = v->x1;
- lxval2 = v->x2;
- lxstep1 = (v->cx - v->x1) / NUMSTEPS;
- lxstep2 = (v->cx - v->x2) / NUMSTEPS;
-
- lyval1 = v->y1;
- lyval2 = v->y2;
- lystep1 = (v->cy - v->y1) / NUMSTEPS;
- lystep2 = (v->cy - v->y2) / NUMSTEPS;
-
-
- // do the first line of the rendering. i.e. furthest from the viewer
- xval = lxval1;
- xstep = (lxval2 - lxval1) / RENDERWIDTH;
- yval = lyval1;
- ystep = (lyval2 - lyval1) / RENDERWIDTH;
- tbuf = buf1;
- per = (HEIGHTMUL << 8) / NUMSTEPS;
-
- for (x = 0; x < RENDERWIDTH; x++)
- {
- i = fINDEX(xval, yval);
- tbuf->y = 99 - (((heightmap[i] - viewer)*per) >> 8);
- tbuf->c = colormap[i];
- xval += xstep;
- yval += ystep;
- tbuf++;
- }
-
- lxval1 += lxstep1;
- lxval2 += lxstep2;
- lyval1 += lystep1;
- lyval2 += lystep2;
-
- // do the intermediate lines
-
- for (y = (FARDEPTH - 1); y > NEARDEPTH; y--)
- {
- xval = lxval1;
- xstep = (lxval2 - lxval1)/ RENDERWIDTH;
- yval = lyval1;
- ystep = (lyval2 - lyval1)/ RENDERWIDTH;
- tbuf = buf2;
- index = 0; // initialize interpolation index
- oldi = -1;
- per = (HEIGHTMUL << 8) / (y - NEARDEPTH); // set perspective value
-
- for (x = 0; x < RENDERWIDTH; x++)
- {
- i = fINDEX(xval, yval);
- if ((oldi != i) || (x == (RENDERWIDTH-1)))
- {
- tbuf->y = 99 - (((heightmap[i] - viewer)*per) >> 8);
- tbuf->c = colormap[i];
- leest[index++] = x;
- oldi = i;
- }
- xval += xstep;
- yval += ystep;
- tbuf++;
- }
-
- // this is where the main smoothing occurs. The height and color are
- // interpolated for maximum effect. Interpolation is done in screen space
- // for maximum speed.
-
- for (x = 0; x <= (index - 2); x++)
- {
- xval = buf2[leest[x]].y << 8;
- yval = buf2[leest[x]].c << 8;
- per = divtable[leest[x+1]-leest[x]];
- xstep = (buf2[leest[x+1]].y - buf2[leest[x]].y) * per;
- ystep = (buf2[leest[x+1]].c - buf2[leest[x]].c) * per;
- tbuf = &buf2[leest[x]+1];
-
- for (i = leest[x]+1; i < leest[x+1]; i++)
- {
- xval+=xstep;
- yval+=ystep;
- tbuf->y = xval >> 8;
- tbuf->c = yval >> 8;
- tbuf++;
- }
- }
-
- // draw the vertical bars
- verticallines();
-
- // swap the buffers
- tbuf = buf1;
- buf1 = buf2;
- buf2 = tbuf;
-
- lxval1 += lxstep1;
- lxval2 += lxstep2;
- lyval1 += lystep1;
- lyval2 += lystep2;
- }
-
- // do the closest row
-
- xval = lxval1;
- xstep = (lxval2 - lxval1) / RENDERWIDTH;
- yval = lyval1;
- ystep = (lyval2 - lyval1) / RENDERWIDTH;
- tbuf = buf2;
- for (x = 0; x < RENDERWIDTH; x++)
- {
- tbuf->y = 199; // set to bottom of the screen so no gaps appear
- tbuf->c = colormap[fINDEX(xval, yval)];
- xval += xstep;
- yval += ystep;
- tbuf++;
- }
- verticallines();
-
- }
-
- void verticallines()
- // draws the vertical lines between two "buffers"
- {
- int x, y;
- int cstep;
- int y1, y2;
- register32_8 cval;
-
- for (x = 0; x < RENDERWIDTH; x++)
- {
- if (buf1[x].y < buf2[x].y)
- {
- y1 = buf1[x].y;
- y2 = buf2[x].y;
- cval.dword = buf1[x].c << 8;
- if ((y2-y1) > 200) // check for divtable over-run
- cstep = ((buf2[x].c - buf1[x].c) << 8) / (y2-y1);
- else
- cstep = ((buf2[x].c - buf1[x].c) * divtable[y2-y1]);
- if (y2 > 199) y2 = 199;
- if (y1 < 0)
- {
- cval.dword += cstep*(-y1);
- y1 = 0;
- }
- y1 = ( (int)(vpage) + (y1 << 8) + (y1 << 6) + x);
- y2 = ( (int)(vpage) + (y2 << 8) + (y2 << 6) + x);
- for (y = y1; y <= y2; y+=320)
- {
- *(char *)(y) = cval.bytes.b2;
- cval.dword+=cstep;
- }
- }
- }
- }
-
-
-
- void makepalette()
- {
- int i;
-
- // For a red landscape
- for (i = 1; i < 64; i++)
- setrgb(i, 16 + (i/4), i/8, 0);
- for (i = 64; i < 128; i++)
- setrgb(i, 32 + ((i-64)/2), i/8, 0);
-
- // For a green Landscape
- // for (i = 1; i < 64; i++)
- // setrgb(i, i/8, 16 + (i/4), 0);
- // for (i = 64; i < 128; i++)
- // setrgb(i, i/8, 32 + ((i-64)/2), 0);
- }
-
- void buildtables()
- {
- int i;
-
- for (i = 1; i < 256; i++)
- divtable[i] = 256/i;
- for (i = 0; i < 256; i++)
- {
- sine[i] = (sin(i*2*3.141592/256.0)*256);
- cosine[i] = (cos(i*2*3.141592/256.0)*256);
- }
- }
-
-
- void createmaps()
- {
- int i;
- srand(MAPSHIFT);
- // allocate necessary memory
- heightmap = (char *)malloc(MAPSIZE*MAPSIZE);
- colormap = (char *)malloc(MAPSIZE*MAPSIZE);
- vpage = (char *)malloc(64000);
- buf1 = (hrec *)malloc(RENDERWIDTH * sizeof(hrec));
- buf2 = (hrec *)malloc(RENDERWIDTH * sizeof(hrec));
-
- // clear all memory
- memset(heightmap, 0, MAPSIZE*MAPSIZE);
- memset(vpage, 0, 64000);
-
- printf("Creating %dx%d fractal terrain\n", MAPSIZE, MAPSIZE);
- heightmap[0] = 64; // initialize starting point on map
- createfractalmap(0, 0, MAPSIZE, MAPSIZE);
-
- printf("Smooting terrain\n");
- for (i = 0; i < NUMSMOOTH; i++)
- smoothmap();
-
- makecolormap();
- }
-
- void cleanup()
- // free allocated memory
- {
- free(heightmap);
- free(colormap);
- free(vpage);
- free(buf1);
- free(buf2);
- }
-
- void createfractalmap(int x1, int y1, int x2, int y2)
- // recursive fractal terrain builder
- {
- int p1, p2, p3, p4;
- int xn, yn, dxy;
-
- if (((x2-x1) < 2) && ((y2-y1) < 2)) // make sure their is something to do
- return;
-
- p1 = heightmap[INDEX(x1,y1)];
- p2 = heightmap[INDEX(x1,y2)];
- p3 = heightmap[INDEX(x2,y1)];
- p4 = heightmap[INDEX(x2,y2)];
-
- xn = (x2+x1) >> 1;
- yn = (y2+y1) >> 1;
- dxy = 5*(x2 - x1 + y2 - y1) / 3;
-
- if (heightmap[INDEX(xn,y1)] == 0)
- heightmap[INDEX(xn,y1)] = newcolor(p1+p3, dxy, 2);
- if (heightmap[INDEX(x1,yn)] == 0)
- heightmap[INDEX(x1,yn)] = newcolor(p2+p4, dxy, 2);
- if (heightmap[INDEX(x2,yn)] == 0)
- heightmap[INDEX(x2,yn)] = newcolor(p3+p4, dxy, 2);
- if (heightmap[INDEX(xn,y2)] == 0)
- heightmap[INDEX(xn,y2)] = newcolor(p1+p2, dxy, 2);
- heightmap[INDEX(xn,yn)] = newcolor(p1+p2+p3+p4, dxy, 4);
- createfractalmap(x1, y1, xn, yn);
- createfractalmap(xn, y1, x2, yn);
- createfractalmap(x1, yn, xn, y2);
- createfractalmap(xn, yn, x2, y2);
- }
-
- char newcolor(int mc, int n, int dvd)
- {
- int loc;
-
- loc = (mc + n - RANDOM(n << 1)) / dvd - 1;
- if (loc > 255)
- loc = 255;
- if (loc < 10)
- loc = 10;
- return(loc);
- }
-
- void smoothmap()
- // smooths the map. Gives better appearence
- {
- int x,y;
-
- for (x = 0; x < MAPSIZE; x++)
- for (y = 0; y < MAPSIZE; y++)
- heightmap[INDEX(x,y)] = (heightmap[INDEX(x-1,y-1)] +
- heightmap[INDEX(x-1,y+1)] +
- heightmap[INDEX(x+1,y-1)] +
- heightmap[INDEX(x+1,y+1)] +
- heightmap[INDEX(x, y-1)] +
- heightmap[INDEX(x, y+1)] +
- heightmap[INDEX(x-1,y)] +
- heightmap[INDEX(x+1,y)]) >> 3;
- }
-
- void makecolormap()
- // builds a color map based on the height map
- // attempts to add some color shift
- {
- int x,y,temp;
-
- for (x = 0; x < MAPSIZE; x++)
- for (y = 0; y < MAPSIZE; y++)
- {
- temp = heightmap[INDEX(x,y)] >> 1;
- if (heightmap[INDEX(x,y-1)] < heightmap[INDEX(x,y)])
- {
- temp -= 5*(heightmap[INDEX(x,y)] - heightmap[INDEX(x, y-1)]);
- if (temp < 0)
- temp = 0;
- }
- else
- {
- if (heightmap[INDEX(x,y-1)] > heightmap[INDEX(x,y)])
- {
- temp += 3*(heightmap[INDEX(x,y-1)] - heightmap[INDEX(x,y)]);
- if (temp > 127)
- temp = 127;
- }
- }
- colormap[INDEX(x,y)] = temp;
- }
- }
-
- void setrgb(char n, char r, char g, char b)
- {
- outp(0x03c8, n);
- outp(0x03c9, r);
- outp(0x03c9, g);
- outp(0x03c9, b);
- }
-
- short int initmouse()
- // checks for mouse driver.
- // returns: 0 - mouse not installed
- // -1 - mouse installed
- {
- union REGS inregs, outregs;
- inregs.w.ax = 0;
- int386(0x33, &inregs, &outregs);
- return(outregs.w.ax);
- }
-
- void readmicky(short int *x, short int *y)
- // returns the mikey count for mouse movement
- {
- union REGS inregs, outregs;
- inregs.w.ax = 0x0b;
- int386(0x33, &inregs, &outregs);
- *x = outregs.w.cx;
- *y = outregs.w.dx;
- }
-
-